package com.gitee.wsl.ext.net

import java.net.Inet4Address
import java.net.Inet6Address
import java.net.InetAddress
import java.nio.ByteBuffer


/**
 * Determine if receiver is available Inet4addresses.
 *
 * @receiver InetAddress
 * @return true: receiver is available Inet4Address, false: otherwise
 */
 fun InetAddress.isAvailableInet4Address() = this is Inet4Address


/**
 * Determine if receiver is available Inet6addresses.
 *
 * @receiver InetAddress
 * @return true: receiver is available Inet6Address, false: otherwise
 */
 fun InetAddress.isAvailableInet6Address() = this is Inet6Address && this.isLinkLocalAddress


/**
 * Returns a combined string of address and port number.
 *
 * @receiver Address to convert
 * @param port port number
 * @return a combined string of address and port number
 */
@Throws(IllegalStateException::class)
fun InetAddress.toAddressString(port: Int): String =
    toAddressString().let {
        if (port == 80 || port <= 0) it else "$it:$port"
    }

private fun InetAddress.toAddressString(): String =
    if (this is Inet6Address) "[${toNormalizedString()}]" else hostAddress

fun InetAddress.toSimpleString(): String =
    if (this is Inet6Address) toNormalizedString() else hostAddress

// VisibleForTesting
fun Inet6Address.toNormalizedString(): String {
    val buffer = ByteBuffer.wrap(address).asShortBuffer()
    val section = IntArray(8) { buffer.get().toInt() and 0xffff }
    val (start, end) = section.findSectionToOmission()
    return buildString {
        for (i in section.indices) {
            if (i < start || i >= end) {
                if (i != 0 && i != end) append(':')
                append(section[i].toString(16))
            } else if (i == start) {
                append("::")
            }
        }
    }
}


private class Range(
    var start: Int = -1,
    var length: Int = 0
)

private fun IntArray.findSectionToOmission(): Pair<Int, Int> {
    var work = Range()
    var best = Range()
    for (i in indices) {
        if (this[i] == 0) {
            if (work.start < 0) work.start = i
            work.length++
        } else if (work.start >= 0) {
            if (work.length > best.length) best = work
            work = Range()
        }
    }
    if (work.length > best.length) best = work
    return if (best.length < 2) -1 to -1 else best.start to best.start + best.length
}
